/*
 * Broadcast.cpp
 *
 *  Created on: 08/ott/2013
 *      Author: alessandrob
 */
#include<iostream>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<opencv2/opencv.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<opencv2/highgui/highgui.hpp>
#include "Utility.h"
#define PORT 5002
#define PORTB 5003
#define BUFLEN

void broadcast(int clientsNumber,int overlap,int loads,int threshold,int blocks)
{
	struct list_nodes nodeList[clientsNumber+1];
	int sockfd,countClientsConnected=0,i,sockBroadcast;
	struct sockaddr_in broadcastAddress,client_address,server_address;
	int broadcastPermission=1,row,dataSent;
	char sync[30];
	char ack[30];
	char ready[5];
	char command[35];
	char* ipAddress;
	float bandwidth;
	double det_time;
	vector<int> params = vector<int>(2);
	vector<KeyPoint> kp,distributedKp;
	Mat descriptors,distributedDescriptors;
	params[0]=0;
	unsigned int slen=sizeof(client_address);
	Mat original,broadcastImage,cameraImage;
	vector<sockaddr_in> clients;
	broadcast_packet packet[blocks+1];
	sync[0]='s';

	//Setting up BRISK
	cout<<"Setting up BRISK..."<<endl;
	Ptr<FeatureDetector> detector;
	detector = new BRISK(threshold,4);
	Ptr<DescriptorExtractor> descriptorExtractor;
	descriptorExtractor = new BRISK(threshold,4);

	cout<<"Struct size: "<<sizeof(packet[0])<<endl;
	cout<<"Setting up UDP server..."<<endl;
	//UDP Socket (used only for the connections)
	if ((sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
		printErrorLog("Server socket creation error. ");
	memset(&server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	//Listening port
	server_address.sin_port = htons(PORT);
	//Binding on port: 5002
	if (bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address))==-1)
		printErrorLog("Wrong binding. The server is NOT running");
	cout<<"Correct binding. The server is working correctly, listening for connections on port: 5002."<< endl;
	//Waiting for the connections
	int CPUspeed;
	uchar endAck[3];
	endAck[0]='e';
	endAck[1]='n';
	endAck[2]='d';
	while(countClientsConnected!=clientsNumber)
	{
		if (recvfrom(sockfd, &ready[0], sizeof(char)*5, 0, (struct sockaddr*)&client_address, &slen)!=sizeof(char)*5)
			printErrorLog("No message received");
		printf("Beaglebone connected: %s:%d\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
		//CPUspeed = deserializeSpeed(ready);
		CPUspeed = (ready[1] << 24);
		CPUspeed = CPUspeed + (ready[2] << 16);
		CPUspeed = CPUspeed + (ready[3] << 8);
		CPUspeed = CPUspeed + (ready[4]);
		nodeList[countClientsConnected+1].CPUspeed = CPUspeed;
		cout<<"CPU speed: "<<nodeList[countClientsConnected+1].CPUspeed<<endl;
		clients.push_back(client_address);
		countClientsConnected++;
	}
	//For all the clients, the server has to test the real bandwidth. So for each client at time, the server
	//creates a new iperf process.
	for(i=0;i<clientsNumber;i++)
	{
		ipAddress = inet_ntoa(clients[i].sin_addr);
		strcpy(command,"iperf -u -c");
		strcat(command,inet_ntoa(clients[i].sin_addr));
		strcat(command," -t 5 -b 54M >/home/ubuntu/iperfLog.txt");
		//system(command);
		//Iperf log file parsing to get the real bandwidth
		bandwidth=getBandwidth(true);
		if(bandwidth==0)
			printErrorLog("Network performance report file doesn't exist");
		nodeList[i+1].ipAddress=ipAddress;
		nodeList[i+1].bandwidth=bandwidth;
	}
	//Camera only
	original = imread("/home/ubuntu/monte.jpg",0);
	detector->detect(original,kp);
	descriptorExtractor->compute(original,kp,descriptors);
	cout<<"Keypoints detected: "<<kp.size()<<endl;
	cout<<"Row: "<<descriptors.rows<<" Columns: "<<descriptors.cols<<endl;
	//Broadcast UDP socket (setting parameters)
	if((sockBroadcast=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))<0)
		cout<<"Broadcast socket correctly created"<<endl;
	if(setsockopt(sockBroadcast,SOL_SOCKET,SO_BROADCAST,&broadcastPermission,sizeof(broadcastPermission))<0)
		printErrorLog("Setting socket option error ");
	//if(setsockopt(sockBroadcast,SOL_SOCKET,SO_REUSEADDR,&broadcastPermission,sizeof(broadcastPermission))<0)
	//	printErrorLog("Setting socket option error ");
	memset(&broadcastAddress, 0, sizeof(broadcastAddress));
	broadcastAddress.sin_family = AF_INET;
	broadcastAddress.sin_addr.s_addr = htonl(INADDR_BROADCAST);
	broadcastAddress.sin_port = htons(PORT);
	//original = imread("/home/ubuntu/manhattan.jpg",0);
	//Clock synchronization
	//Starting timer
	sync[0]='s';
	for(int i=1;i<30;i++)
		sync[i] = 0;
	if (sendto(sockBroadcast, sync, sizeof(char)*30, 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress)) !=sizeof(char)*30)
		printErrorLog("Clock synchronization failed");
	det_time=(double)getTickCount();
	cout<<"Sync ACK sent broadcastly"<<endl;
	//Computing loads
	computeLoadBroadcast(clientsNumber,nodeList,overlap);
	double load_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Compute loads time: T+"<<load_time*1000<<"ms"<<endl;
	//Splitting image
	row = floor((original.rows*nodeList[0].load)/100);
	cout<<"row: "<<row<<endl;
	nodeList[0].width=original.cols;
	nodeList[0].height=row;
	cout<<"From 0 to "<<row<<endl;
	//Image processed by the camera
	overlap = (int)(original.rows*overlap)/100;
	cameraImage = Mat(original,Rect_<int>(0,0,original.cols,row+overlap));
	cout<<"From "<<row<<" to "<<original.rows<<endl;
	imwrite("/home/ubuntu/cameraImage.jpg",cameraImage,params);
	//Image processed by the clients
	broadcastImage=Mat(original,Rect_<int>(0,row,original.cols,original.rows-row));
	imwrite("/home/ubuntu/broadcastImage.jpg",broadcastImage,params);
	double split_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Splitting time: T+"<<split_time*1000<<"ms"<<endl;
	//Create packets
	cout<<"Creating packets..."<<endl;
	int k=0,lastPacket=0;
	int bytesPerPacket = (int)(broadcastImage.cols*broadcastImage.rows)/blocks;
	//Last packet could have different number of bytes
	if(bytesPerPacket*blocks != broadcastImage.cols*broadcastImage.rows)
		lastPacket = broadcastImage.cols*broadcastImage.rows - bytesPerPacket*blocks;
	cout<<"Bytes per packet: "<<bytesPerPacket<<endl;
	cout<<"Last packet: "<<lastPacket<<endl;
	//uchar support[bytesPerPacket];
	for(int i=0;i<blocks;i++)
	{
		packet[i].packetNumber = i+1;
		//normal packets
		packet[i].payloadDimension = bytesPerPacket;
		createHeader(packet,bytesPerPacket,i+1);
		for(int x=0;x<bytesPerPacket;x++,k++)
			packet[i].data[x+8] = broadcastImage.data[k];
	}
	//last packet
	if(lastPacket>0)
	{
		packet[blocks].packetNumber = blocks+1;
		packet[blocks].payloadDimension = lastPacket;
		cout<<"Payload: "<<packet[blocks].payloadDimension<<endl;
		cout<<"Packet number: "<<packet[blocks].packetNumber<<endl;
		createHeader(packet,lastPacket,blocks+1);
		for(int x=0;x<lastPacket;x++,k++)
			packet[blocks].data[x+8] = broadcastImage.data[k];
	}
	double creating_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Creating packets time: T+"<<creating_time*1000<<"ms"<<endl;
	cout<<"Bytes actually copied: "<<k<<endl;
	//disegno immagine
	/*Mat client;
	k=0;
	uchar dataClient[broadcastImage.cols*broadcastImage.rows];
	for(int i=0;i<blocks;i++)
	{
		for(int x=0;x<bytesPerPacket;x++,k++)
			dataClient[k] = packet[i].data[x+8];
	}
	for(int x=0;x<lastPacket;x++,k++)
		dataClient[k] = packet[i].data[x+8];
	cout<<"Bytes: "<<broadcastImage.cols*broadcastImage.rows<<endl;
	cout<<"Bytes actually copied: "<<k<<endl;
	client = Mat(broadcastImage.rows,broadcastImage.cols,CV_8UC1,dataClient,broadcastImage.cols);
	imwrite("/home/ubuntu/broadcastClient.jpg",client,params);*/

	//SENDING PACKETS
	//Sending columns
	uchar infoPacket[12];
	infoPacket[0]=(broadcastImage.cols >> 24);
	infoPacket[1]=(broadcastImage.cols >> 16);
	infoPacket[2]=(broadcastImage.cols >> 8);
	infoPacket[3]=(broadcastImage.cols);
	infoPacket[4]=(broadcastImage.rows >> 24);
	infoPacket[5]=(broadcastImage.rows >> 16);
	infoPacket[6]=(broadcastImage.rows >> 8);
	infoPacket[7]=(broadcastImage.rows);
	infoPacket[8]=(blocks >> 24);
	infoPacket[9]=(blocks >> 16);
	infoPacket[10]=(blocks >> 8);
	infoPacket[11]=(blocks);
	//infoPacket = createInfoPacket(&infoPacket,broadcastImage.cols,broadcastImage.rows,blocks);
	//for(int i=0;i<12;i++)
	//	printf("%d ",infoPacket[i]);
	//printf("\n");
	if(sendto(sockBroadcast, infoPacket, sizeof(char)*12, 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress)) <0)
		printErrorLog("Information packet not sent");
	cout<<"Information packet sent"<<endl;
	/*
	if(sendto(sockBroadcast, &broadcastImage.cols, sizeof(int), 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress)) <0)
		printErrorLog("Columns not sent");
	cout<<"Columns sent"<<endl;
	//Sending rows
	if(sendto(sockBroadcast, &broadcastImage.rows, sizeof(int), 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress)) <0)
		printErrorLog("Rows not sent");
	cout<<"Rows sent"<<endl;
	//Sending number of blocks
	if(sendto(sockBroadcast, &blocks, sizeof(int), 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress)) <0)
		printErrorLog("Number of blocks not sent");
	cout<<"Blocks sent"<<endl;*/
	if(lastPacket>0)
		blocks = blocks + 1;
	//Sending all the blocks
	for(int i=0;i<blocks;i++)
	{
		dataSent = sendto(sockBroadcast, &packet[i].data[0], packet[i].payloadDimension+8, 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress));
		//cout<<dataSent<<endl;
		if(dataSent!=packet[i].payloadDimension+8)
			printErrorLog("Block not sent correctly");
		//else
		//	cout<<"Packet: "<<packet[i].packetNumber<<" sent"<<endl;
	}
	dataSent = sendto(sockBroadcast, endAck, sizeof(char)*3, 0, (struct sockaddr *)&broadcastAddress, sizeof(broadcastAddress));
	if(dataSent!=sizeof(char)*3)
		printErrorLog("End ack not sent correctly");
	double fake_sending_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Fake sending time: T+"<<fake_sending_time*1000<<"ms"<<endl;
	close(sockfd);
	close(sockBroadcast);
	//Re-open the UDP socket
	if ((sockfd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
		printErrorLog("Server socket creation error. ");
	if (bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address))==-1)
		printErrorLog("Wrong binding. The server is NOT running");
	memset(&server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	//Listening port
	server_address.sin_port = htons(PORT);
	cout<<"Waiting for the ACK"<<endl;
	int ackReceived = 0;
	//Waiting for the end of the reception
	while(ackReceived!=clientsNumber)
	{
		if (recvfrom(sockfd, &ack[0], sizeof(char)*30, 0, (struct sockaddr*)&client_address, &slen)!=sizeof(char)*30)
			printErrorLog("No message received");
		if(ack[0]=='p')
		{
			printf("Ack received from: %s:%d\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
			ackReceived++;
		}
	}
	double s_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Sending time: T+"<<s_time*1000<<"ms"<<endl;
	//Processing camera image
	detector->detect(cameraImage,distributedKp);
	descriptorExtractor->compute(cameraImage,distributedKp,distributedDescriptors);
	double p_time = ((double)getTickCount() - det_time)/getTickFrequency();
	cout<<"Server processing time: T+"<<p_time*1000<<"ms"<<endl;
	cout<<"Keypoints found: "<<distributedKp.size()<<endl;
	nodeList[0].keypointsDetected = distributedKp.size();
	for(i=0;i<nodeList[0].keypointsDetected;i++)
	{
		//nodeList[0].keypoints.push_back(distributedKp[i].angle);
		//nodeList[0].keypoints.push_back(distributedKp[i].pt.x);
		nodeList[0].keypoints.push_back(distributedKp[i].pt.x);
		nodeList[0].keypoints.push_back(distributedKp[i].pt.y);
		//nodeList[0].keypoints.push_back(distributedKp[i].size);
	}
	//copying distributed descriptors
	nodeList[0].descriptorsSize = nodeList[0].keypointsDetected*64;
	for(i=0;i<nodeList[0].descriptorsSize;i++)
		nodeList[0].descriptors.push_back(distributedDescriptors.data[i]);
	close(sockfd);

	//RECEIVING DATA FROM THE CLIENTS
	//opening a new TCP socket (only for receiving data)
	if ((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1)
		printErrorLog("Server socket creation error. ");
	cout<<"Server TCP socket created correctly."<<endl;
	memset((char *) &server_address, 0, sizeof(server_address));
	server_address.sin_family = AF_INET;
	server_address.sin_port = htons(PORT);
	server_address.sin_addr.s_addr = htonl(INADDR_ANY);
	//Binding on port: 5002
	if (bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address))==-1)
		printErrorLog("Wrong binding. The server is NOT running");
	cout<<"TCP socket for receiving data. Listening for connections on port: 5002."<< endl;
	//Waiting for connections. All the clients have to connect to the server, before starting the
	//network test performance.
	listen(sockfd,clientsNumber);
	countClientsConnected=0;
	while(countClientsConnected!=clientsNumber)
	{
		if((nodeList[countClientsConnected+1].socket = accept(sockfd,(struct sockaddr*)&client_address,&slen))==-1)
			printErrorLog("Invalid client socket");
		printf("Beaglebone connected: %s:%d\n",inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
		clients.push_back(client_address);
		countClientsConnected++;
	}
	receiveData(clientsNumber,nodeList);
	/*for(int i=0;i<kp.size();i++)
	{
		printf("angle: %f x: %f y: %f size: %f\n",kp[i].angle,kp[i].pt.x,kp[i].pt.y,kp[i].size);
	}*/
	int result = check(clientsNumber,nodeList,kp,descriptors);
	if(result==0)
		cout<<"Check correct"<<endl;
	else if(result==1)
		printErrorLog("Keypoints number not corrected");
	else if(result==2)
		printErrorLog("At least one keypoint different");
	close(sockfd);
}



